`include "common.svh"
`define BUSYTABLE_DIFFTEST
`ifdef BUSYTABLE_DIFFTEST
import "DPI-C" function int busytable_create(
  int id,
  int prf_size,
  string name
);
import "DPI-C" function void busytable_read(
  int id,
  int prf_idx,
  bit prf_valid
);
import "DPI-C" function void busytable_clear(
  int id,
  int prf_idx
);
import "DPI-C" function void busytable_set(
  int id,
  int prf_idx
);

`endif
module busytable #(
    parameter READ_PORTS = 5,
    parameter ALLOCATE_WIDTH = 2,
    parameter RF_WRITE_WIDTH = 3
) (
    input clk,
    input rst,

    input PRF_IDX read_prf[READ_PORTS-1:0],
    output read_prf_valid[READ_PORTS-1:0],

    input clear_valid[ALLOCATE_WIDTH-1:0],
    input PRF_IDX clear_prf[ALLOCATE_WIDTH-1:0],

    input rf_write_valid[RF_WRITE_WIDTH-1:0],
    input PRF_IDX rf_write_prf[RF_WRITE_WIDTH-1:0]

);

  logic [`PRF_SIZE-1:0] valid_table, valid_next, valid_mask, valid_table_clr, valid_table_set;

  genvar gi;
  generate
    for (gi = 0; gi < READ_PORTS; gi = gi + 1) begin : ReadPorts
      assign read_prf_valid[gi] = valid_next[read_prf[gi]];
    end
    for (gi = 0; gi < `PRF_SIZE; gi = gi + 1) begin : ValidTable
      logic [ALLOCATE_WIDTH-1:0] allocate_clr;
      logic [RF_WRITE_WIDTH-1:0] allocate_set;
      always_comb begin
        integer i;
        for (i = 0; i < ALLOCATE_WIDTH; i = i + 1) begin
          allocate_clr[i] = clear_valid[i] & (clear_prf[i] == gi);
        end
        for (i = 0; i < RF_WRITE_WIDTH; i = i + 1) begin
          allocate_set[i] = rf_write_valid[i] & (rf_write_prf[i] == gi);
        end
      end
      assign valid_table_clr[gi] = |allocate_clr;
      assign valid_table_set[gi] = |allocate_set;
    end

  endgenerate
  assign valid_mask = valid_table_clr | valid_table_set;
  assign valid_next = (valid_mask & (valid_table_set)) | ((~valid_mask) & (valid_table));
  reg_r #(`PRF_SIZE, {(`PRF_SIZE) {1'b1}}) valid_table_r (
      .din (valid_next),
      .dout(valid_table),
      .*
  );
`ifdef BUSYTABLE_DIFFTEST
  `define ZEXT(width, val) {{(width-$size(val)){1'b0}},val}
  int id = 0;
  always_ff @(posedge clk) begin
    integer i;
    if (rst) begin
      id <= busytable_create(id, `PRF_SIZE, $sformatf("%m"));
    end else begin
      for (i = 0; i < ALLOCATE_WIDTH; i = i + 1) begin
        if (clear_valid[i])
          busytable_clear(
          id,
          `ZEXT(32, clear_prf[i]));
      end
      for (i = 0; i < RF_WRITE_WIDTH; i = i + 1) begin
        if (rf_write_valid[i])
          busytable_set(
          id,
          `ZEXT(32, rf_write_prf[i]));
      end
      for (i = 0; i < READ_PORTS; i = i + 1) begin
        busytable_read(
        id,
        `ZEXT(32, read_prf[i]),
        read_prf_valid[i]);
      end
    end
  end
`endif
endmodule
